﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RayDen.Library.Components.SystemComponents;
using RayDen.Library.Entity.Interface;
using RayDen.Library.Entity.Renderer;
using RayDen.RayEngine.Core.Interface;

namespace RayDen.RayEngine.Managers
{
    public class StillFrameManager : IFrameManager
    {

        public long TotalSamples
        {
            get { throw new NotImplementedException(); }
        }

        public int Pass
        {
            get { throw new NotImplementedException(); }
        }

        public bool FrameReady
        {
            get { return false; }
        }

        public void SetupNextFrame()
        {
            throw new NotImplementedException();
        }




        public void Complete()
        {
            throw new NotImplementedException();
        }
    }

    public enum RenderingMode
    {
        StillFrame,
        PredefinedSamples,
        PredefinedTime,
        Sequence,
    }

    public class FrameManager : IFrameManager
    {
        private readonly object nextFrameLock = new object();
        private readonly object saveLock = new object();


        public int RenderThreadsCount { get; set; }

        public RenderingMode Mode = RenderingMode.StillFrame;

        private RendererJobInfo RenderJob;
        private AnimationManager animationManager;

        private QualitySettingsInfo quality;
        private OutputSettingsInfo output;
        private DevicesManager devices;
        private IImageFilm film;
        private bool renderingComplete;
        public event Action<FrameManager> OnFrameReady;


        private int currentFrame, maxFrames;
        private int samplesPerFrame;
        private bool endless;

        public long TotalSamples
        {
            get { return this.devices.RenderThreads.Sum(renderThread => renderThread.sampler.TotalSamples); }
        }

        public int TotalFramesToRender
        {
            get { return this.RenderJob.JobFramesCount * this.RenderJob.FramesPerSecond; }
        }

        private bool complete = false;
        public void Complete()
        {
            if (complete)
                return;
            lock (saveLock)
            {
                complete = true;
                var fn = @"F:\Dev\Images\MovieFactory\FrameResult.png";
                film.SavePNG(fn, new string[0]);
                Tracer.TraceLine("Frame rendering result "+fn + "saved");
            }
        }

        public int Pass
        {
            get
            {

#if !CYCLE
                int result = 0;
                for (int i = 0; i < this.devices.RenderThreads.Length; i++)
                {
                    result += devices.RenderThreads[i].sampler.GetPass();
                }

                return result / this.RenderThreadsCount;
#else
                return this.devices.RenderThreads.Sum(renderThread => renderThread.sampler.GetPass()) / this.RenderThreadsCount;
#endif

            }
        }

        private DateTime startTime;
        private TimeSpan renderingTime;

        public bool FrameReady
        {
            get
            {
                if (endless)
                    return false;
                switch (Mode)
                {
                    case RenderingMode.PredefinedSamples:
                    case RenderingMode.Sequence:
                        return
                    Pass >= samplesPerFrame || renderingComplete;

                    case RenderingMode.StillFrame:
                        return false;
                    case RenderingMode.PredefinedTime:
                        return (DateTime.UtcNow - startTime) > renderingTime;

                }


                return
                    Pass >= samplesPerFrame || renderingComplete;
            }
        }

        public FrameManager(IRendererOutputSettings outputSettings, IRendererQualitySettings qualitySettings, RayEngineScene scene, RenderingMode mode = RenderingMode.StillFrame )
        {
            this.output = (OutputSettingsInfo)outputSettings;
            this.quality = (QualitySettingsInfo)qualitySettings;
            this.renderingComplete = false;
            this.samplesPerFrame = quality.SamplesPerPixel * quality.SuperSamplingSize;

            this.RenderJob = new RendererJobInfo(output, quality)
            {
                JobFramesCount = 1,
                FramesPerSecond = 1,
                NameTemplate = "frame_{0}.png",
                OutputDirectory = @"G:\RenderOutput\"
            };
            this.Mode = mode;
            this.startTime = DateTime.UtcNow;
            this.renderingTime = new TimeSpan(0, 5, 0);
            this.endless = this.Mode == RenderingMode.StillFrame;
            this.maxFrames = this.RenderJob.JobFramesCount * this.RenderJob.FramesPerSecond;
            this.animationManager = new AnimationManager(scene, this.TotalFramesToRender);
        }

        public void SetDevices(DevicesManager dev, IImageFilm flm)
        {
            this.devices = dev;
            this.film = flm;
            this.RenderThreadsCount = this.devices.RenderThreads.Length;
        }


        public void SetupNextFrame()
        {
            if (renderingComplete)
                return;
            lock (nextFrameLock)
            {
                devices.IntersectionDevice.Stop();
                Console.WriteLine("Saving {0} frame", currentFrame);
                film.SavePNG(string.Format("{0}\\{1}", this.RenderJob.OutputDirectory, string.Format(this.RenderJob.NameTemplate, this.currentFrame)), new string[0]);
                film.Reset();

                this.currentFrame++;
                this.animationManager.Apply(currentFrame);
                if (this.currentFrame >= this.maxFrames)
                {
                    renderingComplete = true;
                }
                else
                {
                    for (int i = 0; i < this.devices.RenderThreads.Length; i++)
                    {
                        this.devices.RenderThreads[i].Restart();
                    }
                }

                if (this.OnFrameReady != null)
                {
                    this.OnFrameReady(this);
                }
            }
        }



    }
}
